import time
from abc import abstractmethod, ABC
from urllib.parse import urlencode

import requests
from rest_framework.response import Response

from tools.myjwt import mjwt
from user import models, ser


# 单例模式装饰器
def singleton(cls):
    """
    单例模式的装饰器函数，确保一个类只有一个实例

    Args:
        cls: 类本身

    Returns:
        get_instance: 返回类的实例
    """
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance


# 定义社交登录抽象类
class sfLogin(ABC):
    """
    抽象类sfLogin，用于定义社交登录接口
    """
    @abstractmethod
    def get_url(self):
        """
        抽象方法get_url，用于获取登录URL
        """
        pass

    @abstractmethod
    def roll_bcak(self):
        """
        抽象方法roll_bcak，用于处理登录回调
        """
        pass


# 钉钉登录类
class DingDing(sfLogin):
    """
    DingDing类继承sfLogin，实现钉钉登录功能
    """
    _instance = None

    def __new__(cls):
        """
        实现单例模式的构造方法

        Returns:
            cls._instance: 返回钉钉登录类的实例
        """
        if cls._instance is None:
            cls._instance = super(DingDing, cls).__new__(cls)
        return cls._instance

    def get_url(self):
        """
        实现获取钉钉登录URL的方法

        Returns:
            url: 返回构造好的钉钉登录URL
        """
        prompts = {
            'redirect_uri': 'http://127.0.0.1:8080/dingding/',
            'response_type': 'code',
            'client_id': 'ding8zylgumggwzg18vu',
            'scope': 'openid corpid',
            'prompt': 'consent'
        }
        url = "https://login.dingtalk.com/oauth2/auth?" + urlencode(prompts)
        return url

    def roll_bcak(self, request):
        """
        实现钉钉登录回调方法

        Args:
            request: 请求对象，包含回调时的查询参数

        Returns:
            query: 包含用户信息和token的查询字符串列表
        """
        authCode = request.query_params.get('code')
        print(authCode)
        prompts = {
            "clientId": "ding8zylgumggwzg18vu",
            "clientSecret": "vLb31A0QCH-VaEdWG0MJmmNYnwES-OcQmJbjYEmCKKMv-q87R22r4xj5sQd9PXht",
            "code": authCode,
            "grantType": "authorization_code"
        }
        # response_headers = {
        #     'Content-Type': 'application/x-www-form-urlencoded'
        # }
        response_url = 'https://api.dingtalk.com/v1.0/oauth2/userAccessToken'
        response = requests.post(response_url, json=prompts).json()
        accessToken = response.get('accessToken')
        print(response)
        print(accessToken)
        res_url = 'https://api.dingtalk.com/v1.0/contact/users/me'
        res_headers = {"x-acs-dingtalk-access-token": accessToken}
        resp = requests.get(res_url, headers=res_headers).json()
        print(resp)
        name = resp.get('nick')
        uid = resp.get('openId')
        phone = resp.get('mobile')
        # 登录，写入三方登录表和用户表
        sfl = models.Sflogin.objects.filter(uid=uid, types="钉钉").first()
        if not sfl:
            # 写入用户表
            # 手机号用户存在直接绑定,不存在注册新用户
            user = models.User.objects.filter(phone=phone).first()
            if not user:
                userinfo = {
                    "name": name,
                    "phone": phone,
                    "password": phone,
                    "username": phone
                }
                us = ser.UserSerializer(data=userinfo)
                if us.is_valid():
                    user = us.save()
                else:
                    return us.errors
            # 写入三方登录表
            sfinfo = {
                "types": "钉钉",
                "token": accessToken,
                "uid": uid,
                "user": user.id
            }
            sfs = ser.SfloginSerializer(data=sfinfo)
            if sfs.is_valid():
                sfs.save()
            else:
                return sfs.errors
        else:
            user = sfl.user
            sfl.token = accessToken
            sfl.save()

        # 生成jwt token并返回前端
        payload = {"userid": user.id, "username": user.username, "exp": int(time.time()) + 60 * 60 * 2}
        token = mjwt.jwt_encode(payload)
        payload["exp"] = int(time.time() + 60 * 60 * 3)
        retoken = mjwt.jwt_encode(payload)
        query = {
            'userid': payload['userid'],
            'username': payload['username'],
            'token': token,
            'retoken': retoken
        }
        return query


# 工厂类，用于创建不同类型的登录实例
class Factory:
    """
    Factory类用于根据类型创建对应的登录实例
    """
    def create_login(self, types):
        """
        根据类型创建登录实例的方法

        Args:
            types: 登录类型，如"dingding"

        Returns:
            登录实例或抛出不支持的登录类型异常
        """
        if types == "dingding":
            return DingDing()
        else:
            raise ValueError(f"Unsupported login type: template")


# 创建Factory实例
f = Factory()
